<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise源码实现</title>
</head>
<body>
    <script>

        const PENDING = 'pending';
        const FULFILLED = 'fulfilled';
        const REJECT = 'reject';
        /**
         * x 可能为promise或者其他值
        */
        const resolvePromise = (promiseObj,x, resolve, reject)=>{
            if(promiseObj === x ){
                return reject(new TypeError('promise被循环调用了'))
            }
            if (x instanceof MyPromise) {
                // promise
                x.then(resolve,reject)
            }else{
                // 普通值
                resolve(x)
            }
        }

        class MyPromise {
            constructor (executor){
                try {
                    executor(this.resolve,this.reject)
                } catch (error) {
                    this.reject(error)
                }
            }
            // 状态
            status = PENDING
            // 值
            value = undefined
            // 错误
            error = undefined
            // 成功回调，因为可以写多个promise.then，这里声明的是数组
            successCB = []
            // 失败回调，同上
            failCB = []
            
            resolve = val =>{
                if(this.status !== PENDING) return
                // console.log('resolve');
                this.status = FULFILLED
                this.value = val
                if (this.successCB.length) {
                    this.successCB.shift()()
                }

            }

            reject = err =>{
                if(this.status !== PENDING) return
                // console.log('reject');
                this.status = REJECT
                this.error = err
                if (this.failCB.length) {
                    this.failCB.shift()()
                }
            }

            then = (successCB,failCB) =>{
                // console.log('then');
                // 实现then().then(v=>{})这样的无参then给下一个then传递上面的数据
                successCB = successCB || (value => value)
                failCB = failCB || (err => {throw err})

                let p = new MyPromise((res,rej)=>{

                    // 异步情况
                    if(this.status === PENDING){
                        this.successCB.push(()=>{
                            setTimeout(() => {//因为 p 在使用时未能获取，所以这部分代码需要异步
                                try {
                                    let x = successCB(this.value)
                                    // 判断 x 的值是普通值还是promise对象
                                    // 如果是普通值，直接调用resolve
                                    // 如果是promise对象，查看对象结果
                                    // 再根据结果决定嗲用resolve还是reject
                                    resolvePromise(p,x,res,rej)
                                } catch (error) {
                                    rej(error)
                                }
                                
                            }, 0);
                        })
                        this.failCB.push(()=>{
                            setTimeout(() => {//因为 p 在使用时未能获取，所以这部分代码需要异步
                                try {
                                    let x = failCB(this.error)
                                    // 判断 x 的值是普通值还是promise对象
                                    // 如果是普通值，直接调用resolve
                                    // 如果是promise对象，查看对象结果
                                    // 再根据结果决定嗲用resolve还是reject
                                    resolvePromise(p,x,res,rej)
                                } catch (error) {
                                    rej(error)
                                }
                                
                            }, 0);
                        })

                    // 同步resolve情况
                    }else if(this.status === FULFILLED){
                        setTimeout(() => {//因为 p 在使用时未能获取，所以这部分代码需要异步
                            try {
                                let x = successCB(this.value)
                                // 判断 x 的值是普通值还是promise对象
                                // 如果是普通值，直接调用resolve
                                // 如果是promise对象，查看对象结果
                                // 再根据结果决定嗲用resolve还是reject
                                resolvePromise(p,x,res,rej)
                            } catch (error) {
                                rej(error)
                            }
                            
                        }, 0);
                    
                    // 同步reject情况
                    }else if(this.status === REJECT){
                        setTimeout(() => {//因为 p 在使用时未能获取，所以这部分代码需要异步
                            try {
                                let x = failCB(this.error)
                                // 判断 x 的值是普通值还是promise对象
                                // 如果是普通值，直接调用resolve
                                // 如果是promise对象，查看对象结果
                                // 再根据结果决定嗲用resolve还是reject
                                resolvePromise(p,x,res,rej)
                            } catch (error) {
                                rej(error)
                            }
                            
                        }, 0);
                    }

                })

                return p
                
            }
            
            finally = (cb)=>{
                return this.then(val=>{
                    return MyPromise.resolve(cb()).then(()=>val)
                },e=>{
                    return MyPromise.resolve(cb()).then(()=>{throw e})
                    
                })
            }

            catch =(failCB)=> {
                return this.then(undefined, failCB)
            }

            // 使用new MyPromise的时候不可使用，所以要用static
            static all(array){
                // 用于保存promise数组的结果
                let result = []
                // 用于判断是否完成promise
                let idx = 0

                

                return new MyPromise((res,rej)=>{
                    function addData(key,val) {
                        result[key] = val
                        idx++
                        if (idx >= array.length) {
                            res(result)
                        }
                    }
                    for (let i = 0; i < array.length; i++) {
                        const item = array[i];
                        if(item instanceof MyPromise){
                            // promise对象
                            item.then(val=>{addData(i, val)},rej)
                        }else{
                            // 普通值
                            addData(i, item)
                        }
                    }
                })
            }

            static resolve(val){
                if(val instanceof MyPromise){
                    return val
                }else{
                    return new MyPromise(res=>res(val))
                }
            }
        }



        // =================================== 测试 =====================================================



        const p1 = new MyPromise((res,rej)=>{
            // throw new Error('executor error')
            // res(11111)
            setTimeout(()=>res('setTimeout1 resolve'),1000)
            // rej(222)
        })
        const p2 = new MyPromise((res,rej)=>{
            // throw new Error('executor error')
            // res(2222)
            setTimeout(()=>res('setTimeout2 resolve'),1000)
            // rej(222)
        })
        p1.then(
            res=>{
                console.log('then1res:',res);
                throw new Error('then1 error')
            },e=>{
                console.log('then1rej:',e.message);
            }
        )
        // .then(res=>{
        //     console.log('then2res:',res);
        //     throw new Error('then2 error')
        // },e=>{
        //     console.log('then2rej:',e.message);
        // })
        // // p1.then(res=>{
        // //     console.log('2:',res);
        // // })
        
        // .catch(err=>{
        //     console.error('catch:',err)
        // })


        // MyPromise.all(['a','b',p1,2])
        //     .then(res=>{
        //         console.log('all then:',res);
        //     },e=>{
        //         console.warn('all error:',e.message);
        //     })

        
        // MyPromise.resolve(111111).then(console.log)
        // MyPromise.resolve(p1).then(console.log)

        // p1.finally(()=>{
        //     console.log('finally');
        //     // return 'finally'
        //     return p2
        // }).then(res=>{
        //     console.log('finally then:',res);
        // },e=>{
        //     console.log(e);
        // })
    </script>
</body>
</html>